//
//  AdvancedEnumUsage.swift
//  Enum实践
//
//  Created by 我演示 on 2020/5/13.
//  Copyright © 2020 Jerry-Fu. All rights reserved.
//

import UIKit

// 枚举进阶操作

//我已经提及了structs和enums之间的相似性。除了附加方法的能力之外，Swift也允许你在枚举中使用协议(Protocols)和协议扩展(Protocol Extension)。
//
//Swift协议定义一个接口或类型以供其他数据结构来遵循。enum当然也不例外。我们先从Swift标准库中的一个例子开始


//CustomStringConvertible是一个以打印为目的的自定义格式化输出的类型。
protocol CustomStringConvertible {
  var description: String { get }
//     该协议的要求是，针对这个协议，执行getter字节的z类型的字符串操作样式
    
}
// enum 针对每一个case 都有一个对应的switch ，方法遵守这个传递消息
enum Trades: CustomStringConvertible {
   case Buy, Sell
   var description: String {
       switch self {
       case .Buy: return "We're buying something"
       case .Sell: return "We're selling something"
       }
   }
}

let action = Trades.Buy
//print("\(action  )")  // prints: this action is We're buying something


//一些协议的实现可能需要根据内部状态来相应处理要求。例如定义一个管理银行账号的协议。

// 银行卡体系的解读内容
protocol AccountCompatible {
  var remainingFunds: Int { get }
  mutating func addFunds(amount: Int) throws //添加或者删除消息
  mutating func removeFunds(amount: Int) throws
}


enum Account {
  case Empty //空
  case Funds(remaining: Int) //添加

//  enum Error: ErrorType {
//    case Overdraft(amount: Int)
//  }

    // 定义一个属性
  var remainingFunds: Int {
    switch self {
    case .Empty: return 0
    case .Funds(let remaining): return remaining
    }
  }
}

extension Account: AccountCompatible {
    
       mutating func addFunds(amount: Int) throws {
        var newAmout = amount
        if case let .Funds(remaining) = self {
            newAmout += remaining
        }
        
        if newAmout < 0 {
//            throw er
        }
        else if newAmout == 0 {
            self = .Empty
        }
        else {
            self = .Funds(remaining: newAmout)
        }
        
        
    }
    
    mutating func removeFunds(amount: Int) throws {
        try self.addFunds(amount: amount * -1)
    }
    
    
}

//正如你所看见的，我们通过将值存储到enum cases中实现了协议所有要求项。如此做法还有一个妙不可言的地方:现在整个代码基础上你只需要一个模式匹配就能测试空账号输入的情况。你不需要关心剩余资金是否等于零。
// 增加一个模式匹配
//同时，我们也在账号(Accout)中内嵌了一个遵循ErrorType协议的枚举，这样我们就可以使用Swift2.0语法来进行错误处理了。这里给出更详细的使用案例教程。



var account = Account.Funds(remaining: 20) // 这个是初始账号的值信息

//print()try? account.addFunds(amount: 10)  //这个是，再funds的模式匹配，然后进行想加的操作 就是模式匹配
//try? account.removeFunds(amount: 50)


//扩展(Extensions)
//
//正如刚才所见，枚举也可以进行扩展。最明显的用例就是将枚举的case和method分离，这样阅读你的代码能够简单快速地消化掉enum内容，紧接着转移到方法定义:

// 定义枚举
enum Entities {
    case Soldier(x: Int, y: Int)
    case Tank(x: Int, y: Int)
    case Player(x: Int, y: Int)
}

//扩展方法
extension Entities {
   mutating func move(dist: CGVector) {}
   mutating func attack() {}
}

//你同样可以通过写一个扩展来遵循一个特定的协议:
// 扩展遵守对应的协议之后，就会返回对应再这个方法里面，拿到对应的值操作
extension Entities: CustomStringConvertible {
  var description: String {
    switch self {
       case let .Soldier(x, y): return "\(x), \(y)"
       case let .Tank(x, y): return "\(x), \(y)"
       case let .Player(x, y): return "\(x), \(y)"
    }
  }
}


//枚举泛型(Generic Enums)
//枚举也支持泛型参数定义。你可以使用它们以适应枚举中的关联值。就拿直接来自Swift标准库中的简单例子来说，即Optional类型。你主要可能通过以下几种方式使用它:可选链(optional chaining(?))、if-let可选绑定、guard let、或switch，但是从语法角度来说你也可以这么使用Optional

// 解读，Option底层就是Option 的enmu 类型，w内容如下 基本上就是 这种类型的操作

enum MyOptional<T> {
  case Some(T)
  case None
}

//注意枚举的关联值采用泛型参数T作为自身类型，这样可选类型构造任何你想要的返回值。



// 8 : 可以自定义构造方法
// 例如：  在 静态方法 一节当中我们已经提到它们可以作为从不同数据构造枚举的方便形式。在之前的例子里也展示过，对出版社经常误用的苹果设备名返回正确的名字：
enum DeviceThird {
    case AppleWatch
    static func fromSlang(term: String) -> Device? {
      if term == "iWatch" {
      return .AppleWatch
      }
      return nil
    }
}
enum DeviceThirdChange {
    case AppleWatch
    init?(term: String) {
      if term == "iWatch" {
      self = .AppleWatch
      }
      return nil
    }
}
let device = DeviceThirdChange.init(term: "qw")


//8：  更加厉害的改进版本再这里
enum NumberCategory {
   case Small
   case Medium
   case Big
   case Huge
   init(number n: Int) {
    if n < 10000 { self = .Small }
    else if n < 1000000 { self = .Medium }
    else if n < 100000000 { self = .Big }
    else { self = .Huge }
   }
}
let aNumber = NumberCategory(number: 100)


//自定义错误码操作 类型
enum DecodeError: Error {
  case TypeMismatch(expected: String, actual: String)
  case MissingKey(String)
  case Custom(String)
}


// 9 : 观察者模式
/**观察者模式

在 Swift 当中，有许多方法来构建观察模式。如果使用 @objc 兼容标记，则我们可以使用 NSNotificationCenter 或者 KVO。即使不用这个标记，didSet语法也可以很容易地实现简单的观察模式。在这里可以使用枚举，它可以使被观察者的变化更加清晰明了。设想我们要对一个集合进行观察。如果我们稍微思考一下就会发现这只有几种可能的情况：一个或多个项被插入，一个或多个项被删除，一个或多个项被更新。这听起来就是枚举可以完成的工作：
。*/


// 标识的状态码
enum HttpError: String {
  case Code400 = "Bad Request"
  case Code401 = "Unauthorized"
  case Code402 = "Payment Required"
  case Code403 = "Forbidden"
  case Code404 = "Not Found"
}

//结果类型映射(Map Result Types)
//enum JSON {
//    case JSONString(Swift.String)
//    case JSONNumber(Double)
//    case JSONObject([String : JSONValue])
//    case JSONArray([JSONValue])
//    case JSONBool(Bool)
//    case JSONNull
//}


/**游戏也是枚举中的另一个相当好的用例，屏幕上的大多数实体都属于一个特定种族的类型(敌人，障碍，纹理，...)。相对于本地的 iOS 或者 Mac 应用，游戏更像是一个白板。即开发游戏我们可以使用全新的对象以及全新的关联创造一个全新的世界，而 iOS 或者 OSX 需要使用预定义的 UIButtons，UITableViews，UITableViewCells 或者 NSStackView.

不仅如此，由于枚举可以遵守协议，我们可以利用协议扩展和基于协议的编程为不同为游戏定义的枚举增加功能。这里是一个用来展示这种层级的的简短示例
*/
enum FlyingBeast { case Dragon, Hippogriff, Gargoyle }
enum Horde { case Ork, Troll }
enum Player { case Mage, Warrior, Barbarian }
enum NPC { case Vendor, Blacksmith }
enum Element { case Tree, Fence, Stone }

protocol Hurtable {}
protocol Killable {}
protocol Flying {}
protocol Attacking {}
protocol Obstacle {}

extension FlyingBeast: Hurtable, Killable, Flying, Attacking {}
extension Horde: Hurtable, Killable, Attacking {}
extension Player: Hurtable, Obstacle {}
extension NPC: Hurtable {}
extension Element: Obstacle {}


// 使用image 的标志操作 这样子方便定义改版操作，针对每一个按钮进行处理，标识等的

enum DetailViewImages: String {
  case Background = "bg1.png"
  case Sidebar = "sbg.png"
  case ActionButton1 = "btn1_1.png"
  case ActionButton2 = "btn2_1.png"
}


//Rest API 是枚举的绝佳用例。它们都是分组的，它们都是有限的 API 集合，并且它们也可能会有附加的查询或者命名的参数，而这可以使用关联值来实现。
